package com.aispeech.asr.clib.arpc;

import com.sun.jna.Memory;
import com.sun.jna.Native;
import com.sun.jna.Pointer;

import com.sun.jna.ptr.ByteByReference;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class ARpc {
    private static IARpc iaRpc = null;
    private long ptARpc;
    private IARpcCallback callback;
    private final int DEFAULT_TIMEOUT = 5;

    public static void initialize() {
        initialize(2);
    }

    public static void initialize(int loglevel) {
        iaRpc = Native.load("arpc", IARpc.class);

        if (iaRpc == null) {
            throw new RuntimeException("Load ARPC library failed");
        } else {
            int ret = iaRpc.arpc_init(loglevel);
            EARpcState state = EARpcState.from(ret);
            if (state == EARpcState.S_OK) {
                log.info("ARPC library loaded.");
            } else {
                log.error("Load ARPC library failed. state: " + state);
                //throw new RuntimeException("Load ARPC library failed. state: " + state);
            }
        }
    }

    public static void destroy() {
        if (iaRpc != null)
            iaRpc.arpc_finit();
    }

    public static JsonBin parseJsonBinByBytes(byte[] data) {
        Pointer pointer = new Memory(data.length);
        pointer.write(0, data, 0, data.length);

        JsonBinStruct.ByReference struct = iaRpc.jsonbin_parse(pointer, data.length);

        if (struct != null) {
            JsonBin jsonBin = new JsonBin(struct.json);
            if (struct.bin_len > 0) {
                jsonBin.setBin(struct.bin.getPointer().getByteArray(0, struct.bin_len));
            }
            iaRpc.jsonbin_free(struct);

            return jsonBin;
        } else {
            return null;
        }
    }

    public static JsonBin parseJsonBinByStream(Pointer data, int size) {
        JsonBinStruct.ByReference struct = iaRpc.jsonbin_parse(data, size);

        if (struct != null) {
            JsonBin jsonBin = new JsonBin(struct.json);
            if (struct.bin_len > 0) {
                jsonBin.setBin(struct.bin.getPointer().getByteArray(0, struct.bin_len));
            }
            iaRpc.jsonbin_free(struct);

            return jsonBin;
        } else {
            return null;
        }
    }

    public void start(String host, int port, IARpcCallback callback, String params, int timeout) {
        start(host, port, callback, params, timeout, null);
    }

    public void start(String host, int port, IARpcCallback callback, String params, int timeout, String sessionId) {
        this.callback = callback;
        ptARpc = iaRpc.arpc_start(host, port, callback, 0, params, params.length(), timeout, sessionId);
        if (ptARpc <= 0)
            throw new RuntimeException(String.format("connect failed. aiops: %s:%d ", host, port));
    }

    public void startAsync(String host, int port, IARpcCallback callback, String params, int timeout) {
        startAsync(host, port, callback, params, timeout, null);
    }


    public void startAsync(String host, int port, IARpcCallback callback, String params, int timeout, String sessionId) {
        this.callback = callback;
        ptARpc = iaRpc.arpc_start_async(host, port, callback, 0, params, params.length(), timeout, sessionId);
        if (ptARpc <= 0)
            throw new RuntimeException(String.format("connect failed. aiops: %s:%d ", host, port));
    }

//    @Deprecated
//    public int startBlock(String host, int port, IARpcCallback callback, String params, int timeout) {
//    	IntranetCallBack intranetCallBack = new IntranetCallBack(callback);
//    	this.callback = intranetCallBack;
//        ptARpc = iaRpc.arpc_start(host, port, intranetCallBack, 0, params, params.length(), timeout);
//        if (ptARpc <= 0)
//            throw new RuntimeException(String.format("connect failed. aiops: %s:%d ", host, port));
//
//        int state = intranetCallBack.onStart();
//        return state;
//    }

    public EARpcState feed(byte[] data, int length) {
        return EARpcState.from(iaRpc.arpc_feed_async(ptARpc, data, length));
    }

//    public void stop() {
//        stop(true);
//    }

    /**
     * stopAsync 同步阻塞式
     * @param timeout 最长等待stop的超时时间，单位秒
     */
    public ArpcOutput stop() {
    	ArpcOutput output = new ArpcOutput();
    	output.setCode(EARpcState.S_UNKNOWN.getValue());
    	if (ptARpc != 0) {
    		ArpcOutputStruct.ByReference arpcOutputStruct = iaRpc.arpc_stop(ptARpc,DEFAULT_TIMEOUT);
            if (arpcOutputStruct != null) {
            	output.setCode(arpcOutputStruct.status);
                if (arpcOutputStruct.size > 0) {
                    output.setData(arpcOutputStruct.message.getPointer().getByteArray(0, arpcOutputStruct.size));
                }
            }
    	}
        return output;
    }

    /**
     * stopAsync 同步阻塞式
     */
    public ArpcOutput stop(int timeout) {
    	ArpcOutput output = new ArpcOutput();
    	output.setCode(EARpcState.S_UNKNOWN.getValue());
    	if (ptARpc != 0) {
    		ArpcOutputStruct.ByReference arpcOutputStruct = iaRpc.arpc_stop(ptARpc,timeout);
            if (arpcOutputStruct != null) {
            	output.setCode(arpcOutputStruct.status);
                if (arpcOutputStruct.size > 0) {
                    output.setData(arpcOutputStruct.message.getPointer().getByteArray(0, arpcOutputStruct.size));
                }
            }
    	}
        return output;
    }

    /**
     * stopAsync 异步非阻塞式
     */
    public void stopAsync() {
   	 	if (ptARpc != 0) {
            iaRpc.arpc_stop_async(ptARpc, DEFAULT_TIMEOUT);
        }
    }

    /**
     * stopAsync 异步非阻塞式
     * @param timeout 最长等待stop的超时时间，单位秒
     */
    public void stopAsync(int timeout) {
    	 if (ptARpc != 0) {
             iaRpc.arpc_stop_async(ptARpc,timeout);
         }
    }

//    @Deprecated
//    public int stopBlock(boolean close) {
//
//      if (ptARpc != 0) {
//          iaRpc.arpc_stop(ptARpc);
//      }
//
//      if (close)
//          ptARpc = 0;
//
//      int state = ((IntranetCallBack)callback).onStop();
//      return state;
//
//  }


    public void close() {
        if (ptARpc != 0) {
            iaRpc.arpc_close(ptARpc);
        }

        ptARpc = 0;
        this.callback = null;
    }

    public String sessionId() {
        return iaRpc.arpc_sessionid(ptARpc);
    }
}
